/**
 * Handle makes a window or menu draggable.
 * When you attach this to a Window Element or Menu Element,
 * when the user mouses down on the element, they can drag the window or menu around.
 *
 * @author Andrey Anisimov <hercules@inbox.ru>
 * @see core.ui.Gripper, core.ui.DragWindow
 */

import core.ui.Window;
import core.util.Listeners;

class Handle extends Window {
	
	this.Handle = function(parent, x, y, width, height) {
		super(parent, x, y, width, height);
		this.direction = Handle.DIRECTION_ALL;
		this.draginfo = {};
		this.listeners = new Listeners([
            'onBeginDrag',
            'onEndDrag',
            'onDrag'
        ]);
	}

	this.addDragListener = function(listener) {
		this.listeners.add(listener);
	}

	this.removeDragListener = function(listener) {
		this.listeners.remove(listener);
	}

	/**
	 * Sets target window to drag while the handle is dragging.
	 * 
	 * @param target (Window) target window
	 */
	this.setDragWindow = function(wnd) {
		this.target = wnd;
	}
	
	/**
	 * Returns the target window.
	 *
	 * @return Window
	 */
	this.getDragWindow = function() {
		return this.target;
	}
	
	/**
	 * Sets the handle movement direction.
	 * 
	 * Note: the direction is a bitwise combination of the following:
	 * Handle.DIRECTION_NORTH
	 * Handle.DIRECTION_SOUTH
	 * Handle.DIRECTION_EAST
	 * Handle.DIRECTION_WEST
	 * Handle.DIRECTION_ALL
	 * 
	 * @param dir (int) The drag direction.
	 */
	this.setDirection = function(dir) {
		this.direction = dir;
	}
	
	/**
	 * Returns the drag direction.
	 *
	 * @return int
	 */
	this.getDirection = function() {
		return this.direction;
	}
	
	/**
	 * Begins drag with specified coordinates.
	 *
	 * @param event (Event) The event to use when saving initial coords.
	 */
	this.beginDrag = function(event) {
		if (this.target instanceof Window) {
			// Save start drag position
			this.draginfo = {
				'startX' : event.getScreenX(),
				'startY' : event.getScreenY(),
				'targetX' : this.target.getX(),
				'targetY' : this.target.getY(),
				'dragState' : true
			}
		}
		// Start listen mousemove and mouseup events
		this.attachGlobalEvent('mouseup');
		this.attachGlobalEvent('mousemove');
		this.fireBeginDrag();
	}
	
	/**
	 * Performs drag operation.
	 * 
	 * @param x, y (int) new coordinates.
	 */
	this.doDrag = function(x, y) {
		var oldX = this.target.getX();
		var oldY = this.target.getY();
		var newX = this.draginfo.targetX + x - this.draginfo.startX;
		var newY = this.draginfo.targetY + y - this.draginfo.startY;
		this.target.move(newX, newY);
		this.fireDrag(newX - oldX, newY - oldY);
	}
	
	/**
	 * Ends drag operation.
	 */
	this.endDrag = function() {
		if (this.isDragging()) {
			this.detachGlobalEvent('mouseup');
			this.detachGlobalEvent('mousemove');
			this.draginfo.dragState = false;
		}
	}
	
	/**
	 * Returns true if the handle is currently dragging, false otherwise.
	 *
	 * @return bool
	 */
	this.isDragging = function() {
		return this.draginfo.dragState;
	}
	
	/**
	 * Closes handle window.
	 */
	this.close = function() {
		if (this.isDragging()) {
			this.endDrag();
		}
		super.close();
	}

	/**
	 * @protected
	 */
	this.fireBeginDrag = function() {
		this.listeners.call('onBeginDrag', [this]);
	}

	/**
	 * @protected
	 */
	this.fireEndDrag = function() {
		this.listeners.call('onEndDrag', [this]);
	}

	/**
	 * @protected
	 * @param dx
	 * @param dy
	 */
	this.fireDrag = function(dx, dy) {
		this.listeners.call('onDrag', [this, dx, dy]);
	}

	this.onmousedown = function(event) {
		this.beginDrag(event);
	}
	
	this.onmousemove = function(event) {
		if (this.isDragging()) {
			// Apply direction constraints
			var x = this.draginfo.startX;
			var y = this.draginfo.startY;
			var dx = event.getScreenX() - x;
			var dy = event.getScreenY() - y;
			if (dx >= 0) {
				dx *= (this.direction & Handle.DIRECTION_EAST) ? 1 : 0;
			} else {
				dx *= (this.direction & Handle.DIRECTION_WEST) ? 1 : 0;
			}
			if (dy >= 0) {
				dy *= (this.direction & Handle.DIRECTION_NORTH) ? 1 : 0;
			} else {
				dy *= (this.direction & Handle.DIRECTION_SOUTH) ? 1 : 0;
			}
			// Move the target window
			this.doDrag(x + dx, y + dy);
			event.stop();
		}
	}
	
	this.onmouseup = function() {
		if (this.isDragging()) {
			this.endDrag();
			this.fireEndDrag();
		}
	}
}

Handle.DIRECTION_NORTH = 0x1;
Handle.DIRECTION_SOUTH = 0x2;
Handle.DIRECTION_EAST = 0x4;
Handle.DIRECTION_WEST = 0x8;

Handle.DIRECTION_ALL = (
	Handle.DIRECTION_NORTH |
	Handle.DIRECTION_SOUTH |
	Handle.DIRECTION_EAST |
	Handle.DIRECTION_WEST
);
